// Only one file, using lib sfml
#include <SFML/Graphics.hpp>
#include <memory>
#include <iostream>

// HACK: window cons define, the background image is 1000 x 600
#define WIDTH 1000
#define HEIGHT 600
#define WINDOWTITLE "Flappy Bird(maybe not work)"
// HACK: resource cons define, and COPY from original repo
#define RES_BG "./res/bg.png"
#define RES_FLAPPY "./res/flappy.png"
#define RES_PIPE "./res/pipe.png"
#define RES_FONT "./res/flappybird.ttf"

struct FlappyBird final {
  // HACK:also public
  std::shared_ptr<sf::RenderWindow> window;
  float gravity, frame, space;
  int count, score;
  sf::Texture bg, flappy, pipe; // real body
  std::shared_ptr<sf::Sprite> background, bird, pipeBottom, pipeTop;
  std::vector<sf::Sprite> pipes;
  bool gameover, add;

  sf::Font font;
  sf::Text txt_score, txt_gameover;

  void events();
  void draw();
  void game();
  void movePipes();
  void setAnimeBird();
  void moveBird();

  FlappyBird();
  void run(); // app exec
};

FlappyBird::FlappyBird() {
  window = std::make_shared<sf::RenderWindow>(
      sf::VideoMode(WIDTH, HEIGHT), WINDOWTITLE,
      sf::Style::Titlebar | sf::Style::Close);
  window->setPosition(sf::Vector2i(0, 0));
  window->setFramerateLimit(60);

  gravity = frame = {0.f};
  space = {220.f};
  count = {0};
  // load resource file
  bg.loadFromFile(RES_BG);
  flappy.loadFromFile(RES_FLAPPY);
  pipe.loadFromFile(RES_PIPE);
  // end load resource file

  // make object instance
  background = std::make_shared<sf::Sprite>();
  bird = std::make_shared<sf::Sprite>();
  pipeBottom = std::make_shared<sf::Sprite>();
  pipeTop = std::make_shared<sf::Sprite>();
  // end make object instance

  // set texture
  background->setTexture(bg);
  bird->setTexture(flappy);
  pipeBottom->setTexture(pipe);
  pipeTop->setTexture(pipe);
  // end set texture

  bird->setPosition(500.f - flappy.getSize().x / 2.f,
                    300.f - flappy.getSize().y / 2.f);

  bird->setScale(2.f, 2.f);
  bird->setTextureRect(sf::IntRect(0, 0, 34, 24));

  pipeBottom->setScale(1.5f, 1.5f);
  pipeTop->setScale(1.5f, -1.5f);

  gameover = add = {false};
  score = {0};

  font.loadFromFile(RES_FONT);

  txt_gameover.setFont(font);
  txt_gameover.setString("Press SPACE to restart");
  txt_gameover.setPosition(200, 300);
  txt_gameover.setCharacterSize(50);
  txt_gameover.setOutlineThickness(3);

  txt_score.setFont(font);
  txt_score.setString(std::to_string(score));
  txt_score.setPosition(10.f, 20.f);
  txt_score.setCharacterSize(50);
  txt_score.setOutlineThickness(3);
}

void FlappyBird::events() {
  auto event = std::make_shared<sf::Event>();
  while (window->pollEvent(*event)) {
    if (event->type == sf::Event::Closed) {
      window->close();
    }
  }

  if (gameover && sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) {
    score = 0;
    txt_score.setString(std::to_string(score));
    pipes.clear();
    bird->setPosition(500.f - flappy.getSize().x / 2.f,
                      300.f - flappy.getSize().y / 2.f);

    gameover = false;
  }
}

void FlappyBird::draw() {
  window->clear(sf::Color::Black);
  window->draw(*background);

  for (auto &p : pipes) {
    window->draw(p);
  }

  window->draw(*bird);

  if (gameover) {
    window->draw(txt_gameover);
  }

  window->draw(txt_score);
  window->display();
}

void FlappyBird::run() {
  while (window->isOpen()) {
    events();
    game();
    draw();

    ++count;
    if (count == 300) {
      count = 0;
    }
  }
}

void FlappyBird::movePipes() {
  if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
    gravity = -8.f;
    bird->setRotation(-frame - 10.f);
  } else {
    bird->setRotation(frame - 10.f);
  }

  if (count % 150 == 0) {
    int pos = std::rand() % 275 + 175;

    pipeBottom->setPosition(1000, pos + space);
    pipeTop->setPosition(1000, pos);

    pipes.push_back(*pipeBottom);
    pipes.push_back(*pipeTop);
  }

  for (std::size_t i{}; i < pipes.size(); ++i) {
    if (pipes[i].getGlobalBounds().intersects(bird->getGlobalBounds())) {
      bird->move(15.f, 0);

      if (pipes[i].getScale().y < 0) {
        bird->move(0, -15.f);
      } else {
        bird->move(0, 15.f);
      }

      gameover = true;
    }

    if (pipes[i].getPosition().x < -100) {
      pipes.erase(pipes.begin() + i);
    }

    pipes[i].move(-4.f, 0);

    if (pipes[i].getPosition().x == 448 && !add) {
      std::cout << pipes[i].getPosition().x << "\n";
      txt_score.setString(std::to_string(++score));
      add = true;
    } else {
      add = false;
    }
  }
}

void FlappyBird::game() {
  if (!gameover) {
    setAnimeBird();
    moveBird();
    movePipes();
  }
}

void FlappyBird::setAnimeBird(){
  frame += 0.15f;
  if (frame > 3) {
    frame -= 3;
  }
  bird->setTextureRect(sf::IntRect(34 * (int) frame, 0, 34, 24));
}

void FlappyBird::moveBird() {
  bird->move(0, gravity);
  gravity += 0.5f;
}


// ERROR: it is not playable, just like a rendering true bodys
int main() {
  std::srand(std::time(0));
  auto flappy = std::make_shared<FlappyBird>();
  flappy->run();

  return 0;
}
